#include "Tester.h"
#define FOLD 1  //to fold 


void Tester::MainTest() {
    using namespace std;
    
    ////////////////
    //Important parameters
    ////////////////
    
    char inFileName[] = "testFile.wav";                //< !input file
    string outFileName("convertedTestFile_");          //< part of name of output file

    //unsigned int blockSize = 512;                 //< !blockSize
    unsigned int minBlockSize = 512;
    unsigned int maxBlockSize = 512;
    
    int fpass = 3000, fstop = 3070;
    float_type oscillationInPass = 0.001, oscillationInStop = 0.001;
    LPFComputator::convolutionMethodType convolutionMethod = LPFComputator::NORMAL_CPU;
    LPFComputator::envokingOpenCLMethodType envokingOpenCLMethod = LPFComputator::GCD;
    
    
    
    ////////////////
    //Prepare buffers
    ////////////////
    
    float_type** channelsIn, **channelsOut, **block_channelsIn, **block_channelsOut;
    unsigned int samplingRate;
    long long int length;
    int numOfChannels;
    
    FileIOManager fileIOManager;
    fileIOManager.read(inFileName, sizeof(float_type), &length, (void***)(&channelsIn), &numOfChannels, &samplingRate);
    
    block_channelsIn = new float_type* [numOfChannels];
    channelsOut = new float_type* [numOfChannels];
    block_channelsOut = new float_type* [numOfChannels];
    
    for(int i=0; i < numOfChannels; ++i) {
        //block_channelsIn[i]=new float_type[blockSize];
        channelsOut[i]=new float_type[length];
        //block_channelsOut[i]=new float_type[blockSize];
    }
    
    

    //Create LPFComputator
    LPFComputator filter(samplingRate, fpass, fstop, oscillationInPass, oscillationInStop, convolutionMethod, envokingOpenCLMethod );
    
    
    
    ////////////////
    //Print impulse response to file
    ////////////////
    
    int N;
    N=filter.getSizeOfImpulseResponse();
    float_type* h=new float_type[N];
    filter.getImpulseResponse(h);
    printArray(h, filter.getSizeOfImpulseResponse(), true, "h.txt");
    
    
    
    
    
    
    ////////////////////////////////
    //Measuring time
    ////////////////////////////////
    
    //Descriptions of way of testing >>block real-time or offline<<
    vector<string> testDescriptions_;
    string offlineProcessingString("OFFLINE_PROCESSING");   //< offlineProcessingString to compare to other strings
    string blockProcessingString("BLOCK_PROCESSING");
    testDescriptions_.push_back(blockProcessingString);
    testDescriptions_.push_back(offlineProcessingString);
    size_t numOfTesting = testDescriptions_.size();
    
    //Descriptions of convolution method
    LPFComputator::convolutionMethodType convolutionMethods [] = {LPFComputator::NORMAL_CPU, LPFComputator::OPENCL_GPU};
    std::vector<std::string> convolutionMethodDescriptions_;
    convolutionMethodDescriptions_.push_back(string("NORMAL_CPU"));
    convolutionMethodDescriptions_.push_back(string("OPENCL_GPU"));
    //< here put new convolution method
    size_t numOfConvolutionMethod = convolutionMethodDescriptions_.size();
    
    
    
    ////////////////
    //Processing
    ////////////////
    
    for (int i_testing = 0; i_testing < numOfTesting; ++i_testing)
    {
        bool offlineProcessingMade = false;
        unsigned int i_blockSize = minBlockSize;
        while ( i_blockSize <= maxBlockSize && !offlineProcessingMade )
        {
            for ( int i_convolutionMethod = 0; i_convolutionMethod < numOfConvolutionMethod; ++ i_convolutionMethod )
            {
                filter.setParameters(samplingRate, fpass, fstop, oscillationInPass, oscillationInStop, convolutionMethods[i_convolutionMethod]);
                unsigned int numOfBlocks;
                
                //OFFLINE
                if(testDescriptions_[i_testing] == offlineProcessingString)
                {
                    timer_.start();
                    filter.process(channelsIn, channelsOut, length, numOfChannels); //< processing
                    timer_.stop();
                    offlineProcessingMade = true;
                }
                //ONLINE
                else
                {
                    for(int i=0; i < numOfChannels; ++i) {
                        block_channelsIn[i]=new float_type[i_blockSize];
                        //channelsOut[i]=new float_type[length];
                        block_channelsOut[i]=new float_type[i_blockSize];
                    }
                    
                    numOfBlocks = (unsigned int) (length / (long long)i_blockSize);
                    timer_.start();
                    for(int i_block = 0; i_block < numOfBlocks; ++i_block)
                    {
                        for(int i_channel=0; i_channel < numOfChannels; ++i_channel)
                            for(int i_sampleInBlock = 0; i_sampleInBlock < i_blockSize; ++i_sampleInBlock)
                                block_channelsIn[i_channel][i_sampleInBlock] = channelsIn[i_channel][i_sampleInBlock + i_block*i_blockSize];
                        
                        filter.process(block_channelsIn, block_channelsOut, i_blockSize, numOfChannels);
                        
                        for(int i_channel=0; i_channel < numOfChannels; ++i_channel)
                            for(int i_sampleInBlock = 0; i_sampleInBlock < i_blockSize; ++i_sampleInBlock)
                                channelsOut[i_channel][i_sampleInBlock+i_block*i_blockSize] = block_channelsOut[i_channel][i_sampleInBlock];
                    }
                    timer_.stop();
                    
                    for(int i=0; i < numOfChannels; ++i) {
                        delete [] block_channelsIn[i];
                        //channelsOut[i]=new float_type[length];
                        delete [] block_channelsOut[i];
                    }
                }
                
                //Print results
                if(FOLD) {
                    
                    //filter params
                    cout << "Title: " << testDescriptions_[i_testing] << " " << convolutionMethodDescriptions_[i_convolutionMethod] << endl;
                    cout << "--" << endl;
                    cout << "Filter param (fpass, fstop): (" << fpass << ", " << fstop << ")" << endl;
                    cout << "Length of impulse response: N == " << filter.getSizeOfImpulseResponse() << endl;
                    cout << "--" << endl;
                    //testing methods specific
                    cout << "Testing method: " << testDescriptions_[i_testing] << endl;
                    if ( testDescriptions_[i_testing] == blockProcessingString ) {
                        cout  << "Block size: " << i_blockSize << endl;
                        cout  << "Number of blocks: " << numOfBlocks << endl;
                        cout << "Sampling rate: " << samplingRate << endl;
                    }
                    if ( testDescriptions_[i_testing] == offlineProcessingString ) {
                        cout << "Sampling rate: " << samplingRate << endl;
                        cout << "Length of file: " << length / samplingRate << endl;
                    }
                    cout << "--" << endl;
                    //convolution method
                    cout << "Convolution method: " << convolutionMethodDescriptions_[i_convolutionMethod] << endl;
                    cout << "--" << endl;
                    //time results
                    cout << "Time of processing: " << timer_.getTime() << "s" << endl;
                    if ( testDescriptions_[i_testing] == blockProcessingString ) {
                        double timePerBlock = timer_.getTime()/numOfBlocks;
                        cout << "Average time for one block: \t" << timePerBlock << "s\t" << timePerBlock*1000 << "ms\t" << timePerBlock*1000000 << "micro_s" <<endl;
                        double maxTimeForDSP = (double) i_blockSize / (double) samplingRate;
                        cout << "Maximum time for DSP: \t\t\t" << maxTimeForDSP << "s\t" << maxTimeForDSP*1000 << "ms\t" << maxTimeForDSP*1000000 << "micro_s" << endl;
                    }
                    //
                    cout << endl << endl << endl;
                }
                
                //Write result to file
                string fullOutFileName = (outFileName + testDescriptions_[i_testing] + convolutionMethodDescriptions_[i_convolutionMethod] +string(".wav"));
                fileIOManager.write((void**)(channelsOut), fullOutFileName.c_str());
                
                //Reset channelsOut
                for(int i_channel=0; i_channel < numOfChannels; ++i_channel)
                    for(int i_sample = 0; i_sample < length; ++i_sample)
                        channelsOut[i_channel][i_sample] = 0;
            } /* for convolution method  */
            
            cout << "---------------------------------------------" << endl << endl ;
        
            i_blockSize *= 2;
        } /*  for block size  */
    } /*  for testingMethod  */

    


    
    
    ////////////////
    //Cleaning
    ////////////////
    for(int i=0; i<numOfChannels; ++i) {
        delete [] channelsOut[i];
        delete [] channelsIn[i];
        //delete [] block_channelsOut[i];
        //delete [] block_channelsIn[i];
    }
    
}